package spring.data.jpa.freemarker;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.PropertiesLoaderSupport;

/**
 * <p>
 * 描述: 模板容器，用于模板文件的加载和缓存
 * 
 * @since 2019年11月23日 下午3:36:59
 * @author wangjie
 */
public class FreemarkerResourceContext {

    private static final Log logger = LogFactory.getLog(FreemarkerResourceContext.class);

    private static final String DEFAULT_TEMPLATE_LOCATION = "classpath:/sqls";
    private Map<String, Resource> sqlResourcesCache = new ConcurrentHashMap<>();
    private Map<String, Long> lastModifiedCache = new ConcurrentHashMap<>();
    private Properties ftlConfig;
    private URI rootURI;
    private String rootLocation;
    private static final PathMatchingResourcePatternResolver RESOLVER = new PathMatchingResourcePatternResolver();
    private static final FreemarkerPropertiesLoader PROPERTIES_LOADER = new FreemarkerPropertiesLoader();

    public FreemarkerResourceContext(String configPath) throws IOException {
        Resource resource = RESOLVER.getResources(configPath)[0];
        ftlConfig = PROPERTIES_LOADER.loadProperties(resource);
        rootLocation = StringUtils
                .defaultIfBlank(ftlConfig.getProperty("template.location"), DEFAULT_TEMPLATE_LOCATION);
        Resource rootResource = RESOLVER.getResource(rootLocation);
        if (rootResource.exists()) {
            rootURI = rootResource.getURI();
            loadPatternResource(rootLocation, rootURI);
        }
    }

    /**
     * <p>
     * 描述: 加载模版到缓存，其根路径为：DEFAULT_TEMPLATE_LOCATION
     * 
     * @since 2019年11月23日 上午11:31:19
     * @author wangjie
     * @param location
     * @param rootUri
     * @throws IOException
     */
    private void loadPatternResource(String location, URI rootUri) throws IOException {
        Resource resource = RESOLVER.getResource(location);
        if (resource.getFile().isFile()) {
            logger.debug("加载模板：" + location);
            String key = resource.getURI().getPath().replace(rootUri.getPath(), "");
            sqlResourcesCache.put(key, resource);
        } else {
            File dir = resource.getFile();
            String[] subFiles = dir.list();
            for (String file : subFiles) {
                loadPatternResource(location + "/" + file, rootUri);
            }
        }
    }

    public List<Resource> getResourceByName(String... names) {
        List<Resource> resources = new ArrayList<>(names.length);
        for (String name : names) {
            Resource tmp = getResourceByName(name);
            if (tmp != null) {
                resources.add(tmp);
            }
        }
        return resources;
    }

    private Resource getResourceByName(String name) {
        return sqlResourcesCache.get(getRootName(name));
    }

    private String getRootName(String name) {
        String key = name;
        if (!key.startsWith("/")) {
            key = "/" + name;
        }
        return key;
    }

    public List<Resource> reloadAble(final String... fileNames) throws IOException {
        String hotLoad = ftlConfig.getProperty("template.hot_load_enable");
        boolean isHotLoad = Boolean.TRUE.toString().equals(hotLoad);
        List<Resource> resourceList = new ArrayList<>(fileNames.length);
        for (String fileName : fileNames) {
            String key = getRootName(fileName);
            Long lastModified = lastModifiedCache.get(key);
            Resource resource = getResourceByName(key);
            // 是否新增模板文件
            boolean newTeplate = lastModified == null && resource == null;
            // 是否可以重新装载模板文件
            boolean canLoad = (lastModified == null && resource != null) // 第一次加载
                    || (newTeplate && isHotLoad) // 新文件且允许热加载
                    || (lastModified != null && resource != null && resource.lastModified() > lastModified && isHotLoad) // 有修改的文件且允许热加载
            ;

            if (canLoad) {
                if (newTeplate) {
                    // 新增模板文件装载
                    logger.debug("add new template: " + key + " to context");
                    String tmplateLocation = rootLocation + key;
                    loadPatternResource(tmplateLocation, rootURI);
                    Resource newResource = getResourceByName(key);
                    lastModifiedCache.put(key, newResource.lastModified());
                    resourceList.add(newResource);
                } else {
                    lastModifiedCache.put(key, resource.lastModified());
                    resourceList.add(resource);
                }
            }
        }
        return resourceList;
    }

    static class FreemarkerPropertiesLoader extends PropertiesLoaderSupport {

        public Properties loadProperties(Resource location) throws IOException {
            Properties props = new Properties();
            setLocation(location);
            super.loadProperties(props);
            return props;
        }
    }
}
